import { NodeEventType, __private, _decorator, Node, SafeArea, Widget, assetManager, Prefab, AssetManager, instantiate, error, js } from "cc";
import NodeGroup from "../pool/NodeGroup";
import FragmentManager, { AddListener, Layer, RemoveListener } from "../FragmentManager";
import View from "../view/View";
import { FragmentComponent } from "../component/FragmentComponent";
import { ProxyComponent } from "../component/ProxyComponent";

const removeNode = 'node-remove';
const removeView = 'view-remove';

export abstract class Fragment extends Node {

    /** 所在图层 */
    readonly layerType: Layer;

    /** 被移除的界面缓存数据 */
    protected mViewCache = new Map<string, NodeGroup>();

    /** 正在显示界面数据 */
    protected mViewNode = new Map<View, Node>();

    protected mWidget: Widget;

    protected mFragmentComponent: FragmentComponent;

    constructor(layer: Layer, name: string) {
        super(`[${layer}]${name}`);
        this.layerType = layer;
        this.mFragmentComponent = this.addComponent(FragmentComponent);
        const widget: Widget = this.addComponent(Widget);
        widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true;
        widget.left = widget.right = widget.top = widget.bottom = 0;
        widget.alignMode = 2;
        widget.enabled = true;
        this.mWidget = widget;
    }

    async addView(manager: FragmentManager, view: View, listener?: AddListener, params?: any): Promise<Node | null> {
        // 检查缓存中是否存界面
        let nodePool = this.mViewCache.get(view.constructor.name);
        if (nodePool == null) {
            nodePool = new NodeGroup(view.poolComponent);
            this.mViewCache.set(view.constructor.name, nodePool);
        }

        // 创建对应的Node,代码或者Prefab
        const result = view.create(this);
        let node: Node
        if (result instanceof Node) {
            node = result;
        } else {
            let nodeCache = nodePool.get();
            if (!nodeCache) {
                // 优先加载配置的指定资源包中资源，如果没配置则加载默认资源包资源
                const bundle = result.bundle ?? manager.config.defauleBundle;
                let prefab: Prefab;
                if (bundle instanceof AssetManager.Bundle) {
                    prefab = await this.loadPrefab(bundle, result.prefab);
                } else {
                    prefab = await this.loadPrefab((await this.loadBundle(bundle)), result.prefab);
                }
                if (!prefab) {
                    error(`prefab为【${result.prefab}】的loadAsync 失败`);
                    return Promise.resolve(null);
                }
                nodeCache = instantiate(prefab);
                // 是否启动真机安全区域显示
                if (view.safeArea) {
                    nodeCache.addComponent(SafeArea);
                }
            }
            node = nodeCache;
        }

        // 添加代理
        const proxy = node.addComponent(ProxyComponent);
        proxy.active = true;
        proxy.view = view;
        proxy.fragment = this;
        proxy.fragmentManager = manager;

        // 进行触发回到监听
        if (listener && listener.onAddBefore) {
            await listener.onAddBefore(manager, view, node, this, params ?? {});
        } else if (view.isAddBefore) {
            await view.onAddBefore(manager, view, node, this, params ?? {});
        }

        this.addChild(node);

        // 回调监听 
        if (listener && listener.onAdd) {
            listener.onAdd(manager, view, node);
        } else if (view.isAdd) {
            view.onAdd(manager, view, node);
        }

        // 添加记录
        this.mViewNode.set(view, node);

        const emitRemoveListener = () => {
            this.removeView(manager, view, false);
        }
        const emitDestroyListener = () => {
            this.removeView(manager, view, true);
        }
        // 监听移除PARENTCHANGED
        node.once(removeNode, emitRemoveListener, this);
        node.once(NodeEventType.NODE_DESTROYED, emitDestroyListener, this);

        // 添加监听
        node.once(removeView, () => {
            // console.log('view-remove before>> ',
            //     node.hasEventListener(Fragment.NodeEventType.NODE_REMOVE),
            //     node.hasEventListener(NodeEventType.NODE_DESTROYED),
            //     node.hasEventListener('view-remove'))

            node.off(removeNode, emitRemoveListener, this);
            node.off(NodeEventType.NODE_DESTROYED, emitDestroyListener, this);

            // console.log('view-remove after>> ',
            //     node.hasEventListener(Fragment.NodeEventType.NODE_REMOVE),
            //     node.hasEventListener(NodeEventType.NODE_DESTROYED),
            //     node.hasEventListener('view-remove'))
        })
        return Promise.resolve(node);
    }

    async removeView(manager: FragmentManager, view: View, destroy: boolean = true, listener?: RemoveListener, params?: any) {
        const node = this.mViewNode.get(view);
        if (!node) {
            return false;
        }
        // 进行移除操作
        this.mViewNode.delete(view);

        // 发送移除事件
        node.emit(removeView);

        // 回调监听 移除前监听
        if (listener && listener.onRemoveBefore) {
            await listener.onRemoveBefore(manager, view, node, this, params ?? {});
        } else if (view.isRemoveBefore) {
            await view.onRemoveBefore(manager, view, node, this, params ?? {});
        }

        if (destroy) {
            node.destroy();
        } else {
            const nodePool = this.mViewCache.get(view.constructor.name);
            if (nodePool) {
                nodePool.put(node);
            }
        }

        // 回调监听 移除后监听
        if (listener && listener.onRemove) {
            listener.onRemove(manager, view, node, destroy);
        } else if (view.isRemove) {
            view.onRemove(manager, view, node, destroy);
        }

        // 进行销毁
        const proxy = node.getComponent(ProxyComponent)
        if (proxy) {
            proxy.destroy();
            proxy.active = false;
            proxy.view = null!;
            proxy.fragment = null!;
            proxy.fragmentManager = null!;
        }
        return true;
    }

    clear(manager: FragmentManager, destroy: boolean) {
        // 清除所有显示的界面
        for (const element of this.mViewNode) {
            this.removeView(manager, element[0])
        }
        this.mViewNode.clear();

        // 清除缓存中的界面
        if (destroy) {
            for (const element of this.mViewCache) {
                element[1].clear(destroy);
            }
            this.mViewCache.clear()
        }
    }

    private async loadBundle(bundleName: string): Promise<AssetManager.Bundle> {
        return new Promise(async (resolve) => {
            let bundle = assetManager.bundles.get(bundleName);
            // 获取缓存中的资源包
            if (!bundle) {
                // 自动加载资源包
                bundle = await new Promise<AssetManager.Bundle>((resolve, reject) => {
                    assetManager.loadBundle(bundleName, (err, bundle: AssetManager.Bundle) => {
                        if (err) {
                            error('loadBundle::Error', bundleName, err);
                            reject(err);
                            return;
                        }
                        resolve(bundle);
                    });
                });
            }
            resolve(bundle);
        });
    }

    private async loadPrefab(bundle: AssetManager.Bundle, path: string): Promise<Prefab> {
        return new Promise(async (resolve, rejuse) => {
            // 获取缓存中的资源
            bundle.load(path, Prefab, (err: Error | null, data: Prefab) => {
                if (err) {
                    error('loadAsset::Error', bundle, path, err);
                    rejuse(err);
                    return;
                }
                resolve(data);
            });
        });
    }


    protected scheduleOnce(callback: () => void, time: number) {
        this.mFragmentComponent.scheduleOnce(callback, time);
    }
}

/** 节点Fragment功能 */
//@ts-ignore
if (!Node.prototype["__$NodeFragmentExt$__"]) {
    //@ts-ignore
    Node.prototype["__$NodeFragmentExt$__"] = true;
    //----------------   Node 添加 Fragment 操作方法 ----------------
    js.mixin(Node.prototype, {
        removeFromFragment: function () {
            this.emit(removeNode, this);
        }
    });
}

declare module "cc" {
    // 这里使用 interface 进行扩展，如果使用 class 则会与现有的 d.ts 有冲突
    export interface Node {
        /** 开始拖拽 */
        removeFromFragment(): void;
    }
}